package com.base.custom

import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatTextView
import com.base.R
import kotlin.math.abs
import kotlin.math.floor
import kotlin.math.sin

/**
 * 侧边带字母导航栏实现
 */
class SideBar(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0): AppCompatTextView(context,attrs,defStyleAttr) {
    private var letters = arrayOf("↑", "☆", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "#")
    private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var bigTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)


    private var eventY = 0f
    private var w = 0f
    private var sideTextWidth = 0f

    /**
     * 是否重新测量宽高
     */
    private var isTouching = false
    private var itemH = 0f

    /**
     * 振幅
     */
    private var amplitude = dp(100).toFloat()

    /**
     * 波峰与bigText之间的距离
     */
    private var gapBetweenText = dp(50)

    /**
     * 开口数量
     */
    private var openCount = 13

    /**
     * 字体缩放，基于textSize
     */
    private var fontScale = 1f
    private var bigTextSize = 0f

    /**
     * 选中字体颜色
     */
    private var selectedColor = Color.BLACK


    constructor(context: Context): this(context,null)

    constructor(context: Context,attrs: AttributeSet?): this(context,attrs,0)

    init {
        if (attrs != null) {
            val typedArray = context.obtainStyledAttributes(attrs, R.styleable.SideBar)
            amplitude = typedArray.getInteger(R.styleable.SideBar_amplitude, dp(100)).toFloat()
            fontScale = typedArray.getFloat(R.styleable.SideBar_fontScale, 1f)
            bigTextSize = typedArray.getFloat(R.styleable.SideBar_bigTextSize, textSize * 3)
            gapBetweenText = typedArray.getInteger(R.styleable.SideBar_gapBetweenText, dp(50))
            openCount = typedArray.getInteger(R.styleable.SideBar_openCount, 13)
            selectedColor = typedArray.getColor(R.styleable.SideBar_selectedColor,Color.BLACK)
            typedArray.recycle()
        }
        bigTextSize = textSize * 3
        textPaint.color = currentTextColor
        textPaint.textSize = textSize
        textPaint.textAlign = Paint.Align.CENTER
        bigTextPaint.color = selectedColor
        bigTextPaint.textSize = bigTextSize
        bigTextPaint.textAlign = Paint.Align.CENTER
        sideTextWidth = textPaint.measureText("W")
    }


    fun setFontScale(fontScale: Float): SideBar {
        this.fontScale = fontScale
        return this
    }

    override fun setTextColor(color: Int) {
        super.setTextColor(color)
        textPaint.color = color
    }

    override fun setTextColor(colors: ColorStateList?) {
        super.setTextColor(colors)
        textPaint.color = currentTextColor
    }

    fun setSelectedColor(color: Int){
        selectedColor = color
        bigTextPaint.color = color
    }

    fun setDataResource(data: Array<String>) {
        letters = data
        invalidate()
    }


    fun setBigTextSize(bigTextSize: Float): SideBar? {
        this.bigTextSize = bigTextSize
        bigTextPaint.textSize = bigTextSize
        return this
    }

    fun setAmplitude(amplitude: Float): SideBar? {
        this.amplitude = amplitude
        return this
    }

    fun setGapBetweenText(gapBetweenText: Int): SideBar? {
        this.gapBetweenText = gapBetweenText
        return this
    }

    fun setOpenCount(openCount: Int): SideBar? {
        this.openCount = openCount
        return this
    }

    private fun measureAW(height: Int) {
        itemH = (height - paddingTop - paddingBottom).toFloat() / letters.size
        // 开口宽度
        val width = itemH * openCount
        // 角速度 2PI/t 周期
        w = (Math.PI * 2.0f / (width * 2)).toFloat()
    }


    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val mode = MeasureSpec.getMode(widthMeasureSpec)
        var viewWidth = MeasureSpec.getSize(widthMeasureSpec)
        measureAW(MeasureSpec.getSize(heightMeasureSpec))
        if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
            viewWidth = if (!isTouching) (sideTextWidth + paddingLeft + paddingRight).toInt() else (amplitude + gapBetweenText + getBigTextWidth() + paddingLeft + paddingRight).toInt()
        }
        setMeasuredDimension(viewWidth, MeasureSpec.getSize(heightMeasureSpec))
    }



    private fun dp(v: Int): Int {
        val scale = context.resources.displayMetrics.density
        return (v * scale + 0.5f).toInt()
    }



    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        val startTouchX = (measuredWidth - amplitude).toInt()
        when (event.action) {
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
                if (event.x > startTouchX) {
                    eventY = event.y
                    if (!isTouching) {
                        isTouching = true
                        requestLayout()
                    } else {
                        invalidate()
                    }
                } else {
                    if (isTouching) {
                        resetDefault()
                    }
                }
                return true
            }
            MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
                resetDefault()
                return true
            }
        }
        return super.onTouchEvent(event)
    }

    private fun resetDefault() {
        isTouching = false
        eventY = 0f
        requestLayout()
    }


    override fun onDraw(canvas: Canvas) {
        val singleSideCount = openCount / 2
        var index = if (isTouching && eventY >= paddingTop && eventY <= measuredHeight-paddingTop-paddingBottom) floor((eventY / itemH).toDouble()).toInt() else -(singleSideCount + 1)
        if(index >= letters.size)index = letters.size - 1
        val sideX = sideTextWidth / 2 + paddingRight

        for (i in letters.indices) {
            textPaint.textSize = textSize
            val y = (itemH * (i + 1) + paddingTop).toInt()
            var x: Int
            if (abs(i - index) > singleSideCount) {
                x = (measuredWidth - sideX).toInt()
            } else {
                val percent = eventY / itemH
                val t = (i * itemH + paddingTop - eventY).toInt()
                var v = amplitude * sin(w * t + Math.PI / 2)

                //如果算出来小于字体宽度 就取字体宽度
                v = v.coerceAtLeast(sideX.toDouble())
                x = (measuredWidth - v).toInt()
                //根据delta缩放字体
                if (v == sideX.toDouble()) {
                    textPaint.textSize = textSize
                } else {
                    val delta = abs(i - percent) / singleSideCount
                    textPaint.textSize = textSize + (1 - delta) * textSize * fontScale
                }
            }
            canvas.drawText(letters[i], x.toFloat(), y.toFloat(), textPaint)
        }
        if (index != -(singleSideCount + 1)) {
            canvas.drawText(letters[index], paddingLeft + getBigTextWidth() / 2, paddingTop + itemH * (index + 1), bigTextPaint)
            call?.onSelect(index,letters[index])
        }
    }

    private fun getBigTextWidth(): Float {
        return bigTextPaint.measureText("W")
    }

    interface SelectCall{
        fun onSelect(index: Int, select: String)
    }
    private var call: SelectCall? = null
        set(value) = setSelectCall(value)
    fun setSelectCall(call: SelectCall?) {
        this.call = call
    }
}